package sojo.com.inverter.utils;

import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @author cheng_hu
 * @description:
 * @date 2023-02-23 18:54:55
 */

public class ToolsUtil {

    private static List<String> time = Arrays.asList("0", "5");

    /**
     * urlKey
     */
    public static String urlKey(Socket socket) {
        return socket.getInetAddress().toString() + "-" + socket.getPort();

    }


    public static String getRandomTimeId(int len) {
        StringBuffer b = new StringBuffer();
        SimpleDateFormat allTime = new SimpleDateFormat("YYYYMMddHHmmSSS");
        String strId = allTime.format(new Date());
        b.append(strId);
        int k = len - strId.length();
        Random random = new Random();
        for (int i = 0; i < k; i++) {
            int m = random.nextInt(9) + 1;
            b.append(String.valueOf(m));
        }

        return b.toString();
    }


    public static String getRandomId(int len) {

        StringBuffer b = new StringBuffer();
        Long id = new Date().getTime();
        String strId = String.valueOf(id).substring(4);
        b.append(strId);
        int k = len - 9;
        Random random = new Random();
        for (int i = 0; i < k; i++) {
            int m = random.nextInt(9) + 1;
            b.append(String.valueOf(m));
        }
        return b.toString();
    }

    /* 根据时间戳生成的唯一值 */
    public static String getTimeNum() {
        Random random = new Random();
        SimpleDateFormat allTime = new SimpleDateFormat("YYYYMMddHHmmSSS");
        String num = allTime.format(new Date()) + random.nextInt(10);
        return (num + random.nextInt(10) + random.nextInt(10)).substring(2);
    }


    /**
     * 字节转换成二进制
     */
    public static String byteChangeBinaary(byte b) {
        return "" + (byte) (b >> 7 & 0x1) + (byte) (b >> 6 & 0x1) + (byte) (b >> 5 & 0x1) + (byte) (b >> 4 & 0x1) + (byte) (b >> 3 & 0x1) + (byte) (b >> 2 & 0x1) + (byte) (b >> 1 & 0x1) + (byte) (b & 0x1);
    }

    /**
     * 截取数组
     *
     * @param sourceArray 源数组
     * @param sourceIndex 起始位置
     * @param length      长度
     * @return 截取的数组
     */
    public static byte[] truncateArray(byte[] sourceArray, int sourceIndex, int length) {
        if (sourceArray == null && sourceArray.length < sourceIndex + 1) {
            return null;
        }
        /*原数组数据长度不足，取所有剩下的值*/
        if (sourceArray.length < sourceIndex + length) {
            byte[] arrays = new byte[sourceArray.length - sourceIndex];
            System.arraycopy(sourceArray, sourceIndex, arrays, 0, sourceArray.length - sourceIndex);
            return arrays;
        } else {
            byte[] arrays = new byte[length];
            System.arraycopy(sourceArray, sourceIndex, arrays, 0, length);
            return arrays;
        }
    }

    /**
     * 将byte转换为一个长度为8的byte数组，数组每个值代表bit
     */
    public static byte[] getBooleanArray(byte b) {
        byte[] array = new byte[8];
        for (int i = 7; i >= 0; i--) {
            array[i] = (byte) (b & 1);
            b = (byte) (b >> 1);
        }
        return array;
    }

    /**
     * 将字节数组转化为ASCII 字符串
     *
     * @param bytes
     * @param start
     * @param length
     * @return
     */
    public static String asciiToString(byte[] bytes, int start, int length) {
        if (bytes == null || bytes.length < start + length + 1) {
            return null;
        }
        String string = new String();
        for (int i = start; i < start + length; i++) {
            if (bytes[i] != 0x00) {
                string += String.valueOf((char) bytes[i]);
            }
        }
        return string;
    }

    /**
     * 将字节数组转化为ASCII 字符串
     *
     * @param bytes
     * @param start
     * @param length
     * @return
     */
    public static String asciiToString2(byte[] bytes, int start, int length) {
        try {
            String text = new String(bytes, start, length, "ASCII");
            return text;
        } catch (Exception e) {
            return "0";
        }
    }

    /**
     * 16进制的字符串表示转成字节数组
     *
     * @param hexString 16进制格式的字符串
     * @return 转换后的字节数组
     **/
    public static byte[] stringToByteArray(String hexString) {

        if (StringUtils.isEmpty(hexString))
            throw new IllegalArgumentException("this hexString must not be empty");

        hexString = hexString.toLowerCase();
        final byte[] byteArray = new byte[hexString.length() / 2];
        int k = 0;
        for (int i = 0; i < byteArray.length; i++) {//因为是16进制，最多只会占用4位，转换成字节需要两个16进制的字符，高位在先
            byte high = (byte) (Character.digit(hexString.charAt(k), 16) & 0xff);
            byte low = (byte) (Character.digit(hexString.charAt(k + 1), 16) & 0xff);
            byteArray[i] = (byte) (high << 4 | low);
            k += 2;
        }
        return byteArray;
    }

    /**
     * 字节数组转成16进制表示格式的字符串
     *
     * @param byteArray 需要转换的字节数组
     * @return 16进制表示格式的字符串
     **/
    public static String byteArrayToHexString(byte[] byteArray, int start, int length) {
        if (byteArray == null || byteArray.length < 1 || byteArray.length < start + length)
            throw new IllegalArgumentException("this byteArray must not be null or empty");

        final StringBuilder hexString = new StringBuilder();
        for (int i = start; i < start + length; i++) {
            if ((byteArray[i] & 0xff) < 0x10)//0~F前面不零
                hexString.append("0");
            hexString.append(Integer.toHexString(0xFF & byteArray[i]));
        }
        return hexString.toString().toLowerCase();
    }

    /**
     * 字节数组转成16进制表示格式的字符串 加空格
     *
     * @param byteArray 需要转换的字节数组
     * @return 16进制表示格式的字符串
     **/
    public static String byteArrayToHexSpace(byte[] byteArray, int start, int length) {
        if (byteArray == null || byteArray.length < 1 || byteArray.length < start + length)
            throw new IllegalArgumentException("this byteArray must not be null or empty");

        final StringBuilder hexString = new StringBuilder();
        for (int i = start; i < start + length; i++) {
            if ((byteArray[i] & 0xff) < 0x10)//0~F前面不零
                hexString.append("0");
            hexString.append(Integer.toHexString(0xFF & byteArray[i]));
            hexString.append(" ");
        }
        return hexString.toString().toLowerCase();
    }

    /**
     * 将int数值转换为占四个字节的byte数组，本方法适用于(低位在前，高位在后)的顺序
     *
     * @param value  要转换的int值
     * @param length 长度
     * @return byte数组
     */
    public static byte[] intToBytesLow(int value, int length) {
        byte[] src = new byte[length];
        for (int j = 0; j < length; j++) {
            src[length - j - 1] = (byte) ((value >> (length - j - 1) * 8) & 0xFF);
        }
        return src;
    }


    /**
     * 将int数值转换为占四个字节的byte数组，本方法适用于(高位在前，低位在后)的顺序。
     *
     * @param value  要转换的int值
     * @param length 长度
     * @return byte数组
     */
    public static byte[] intToBytesHigh(int value, int length) {
        byte[] src = new byte[length];
        for (int j = 0; j < length; j++) {
            src[j] = (byte) ((value >> (length - j - 1) * 8) & 0xFF);
        }
        return src;
    }


    /**
     * byte数组中取long数值，本方法适用于(低位在前，高位在后)的顺序。
     *
     * @param src byte数组
     * @param start 从多少下标开始取
     * @param length 长度
     * @return
     */
    public static long bytesToLongLow(byte[] src, int start, int length) {
        long value = 0;
        for (int i = 0; i < length; i++) {
            value = value | ((long) (src[start + i] & 0xFF) << (i * 8));
        }
        return value;
    }

    /**
     * byte数组中取int数值，本方法适用于(低位在后，高位在前)的顺序。
     *
     * @param src byte数组
     * @param start 从多少下标开始取
     * @param length 长度
     * @return
     */
    public static long bytesToLongHigh(byte[] src, int start, int length) {
        long value = 0;
        for (int i = 0; i < length; i++) {
            value = value | ((long) (src[length - i + start - 1] & 0xFF) << (i * 8));
        }
        return value;
    }


    /**
     * byte数组中取int数值，本方法适用于(低位在前，高位在后)的顺序。
     *
     * @param src    byte数组
     * @param start  从数组的第offset位开始
     * @param length 长度
     * @return int数值 0===
     */
    public static int bytesToIntLow(byte[] src, int start, int length) {
        int value = 0;
        for (int i = 0; i < length; i++) {
            value = value | ((src[start + i] & 0xFF) << (i * 8));
        }
        return value;
    }

    /**
     * byte数组中取 有符号 int数值，本方法适用于(低位在前，高位在后)的顺序。
     *
     * @param src    byte数组
     * @param start  从数组的第offset位开始
     * @param length 长度
     * @return int数值 0===
     */
    public static int bytesToUnIntLow(byte[] src, int start, int length) {
        int value = 0;
        for (int i = 0; i < length; i++) {
            value = value | ((src[start + i]) << (i * 8));
        }
        return value;
    }

    /**
     * byte数组中取int数值，本方法适用于(低位在后，高位在前)的顺序。
     *
     * @param src    byte数组
     * @param start  从数组的第offset位开始
     * @param length 长度
     */
    public static int bytesToIntHigh(byte[] src, int start, int length) {
        int value = 0;
        for (int i = 0; i < length; i++) {
            value = value | ((src[length - i + start - 1] & 0xFF) << (i * 8));
        }
        return value;
    }

    /**
     * byte数组中取 有符号int值 低位在后，高位在前*
     * @param src byte数组
     * @param start 从数组的第offset位开始
     * @param length 长度
     * @return
     */
    public static int bytesToUnIntHigh(byte[] src, int start, int length) {
        int value = 0;
        for (int i = 0; i < length; i++) {
            value = value | ((src[length - i + start - 1]) << (i * 8));
        }
        return value;
    }

    public static String binaryToDecimal(int n) {
        String str = "";
        while (n != 0) {
            str = n % 2 + str;
            n = n / 2;
        }
        return str;
    }

    /**
     * 恒信创获取校验和。
     * 0x100 = 256 = 1000 0000
     *
     * @param bytes    数组
     * @param startNum 开始位置
     * @param count    长度
     */
    public static byte msgCheckSum(byte[] bytes, int startNum, int count) {
        int iSum = 0;
        for (int i = startNum; i < startNum + count; i++) {
            iSum += bytes[i];
        }
        iSum %= 0x100;

        return (byte) iSum;
    }

    /**
     * 将字符串加入数组
     *
     * @param string 字符串
     * @param bytes  待加入数组
     * @param start  开始位置
     * @param length 长度
     * @return 是否成功
     */
    public static boolean stringIntoBytes(String string, byte[] bytes, int start, int length) {
        byte[] stringBytes = stringToByteArray(string);
        if (stringBytes.length != length || bytes.length < start + length) {
            return false;
        }
        System.arraycopy(stringBytes, 0, bytes, start, length);
        return true;
    }

    /**
     * 字节转时间
     *
     * @param bytes 字节数组
     * @param start 开始位置
     * @return 时间
     */
    public static Date byteToDate(byte[] bytes, int start) {

        StringBuffer strDate = new StringBuffer();
        Date date;
        for (int i = start; i < start + 7; i++) {
            strDate.append(String.format("%02d", bytes[i]));
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        try {
            date = sdf.parse(strDate.toString());
        } catch (Exception e) {
            return null;
        }

        return date;
    }

    /**
     * 时间转字节
     *
     * @param date 时间
     * @return 字节
     */
    public static byte[] dateToBytes(Date date) {
        byte[] bytes = new byte[7];
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String strData = sdf.format(date);
        for (int i = 0; i < 7; i++) {
            bytes[i] = Byte.valueOf(strData.substring(i * 2, i * 2 + 2));
        }
        return bytes;
    }

    /**
     * cp56time2a 转时间，8个字节
     *
     * @param bytes
     * @return
     */
    public static Date getCarDate(byte[] bytes, int k) {
        int start = k;
        Calendar cal = Calendar.getInstance();
        cal.clear();
        cal.set(Calendar.YEAR, bytesToIntHigh(bytes, start, 1) + 2000);
        cal.set(Calendar.MONTH, bytesToIntHigh(bytes, start += 1, 1) - 1);
        cal.set(Calendar.DAY_OF_MONTH, bytesToIntHigh(bytes, start += 1, 1));
        cal.set(Calendar.HOUR_OF_DAY, bytesToIntHigh(bytes, start += 1, 1));
        cal.set(Calendar.MINUTE, bytesToIntHigh(bytes, start += 1, 1));
        cal.set(Calendar.SECOND, bytesToIntHigh(bytes, start + 1, 1));
        return cal.getTime();
    }


    /**
     * 将时间格式为(整五分钟)  2022-07-08 15:05:00.0
     */
    public static Timestamp getTimeStamp(Date date){

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:00");
        String formatDate = sdf.format(date);

        String[] splitDate = formatDate.split(":");
        String substring1 = splitDate[1].substring(0,1);//分钟的十位
        String substring2 = splitDate[1].substring(1,2);//分钟的个位

        if (!time.contains(substring2)){
            Long aLong = Long.valueOf(substring2);
            if (aLong > 0 && aLong <5){
                substring2 = "0";
            }else {
                substring2 = "5";
            }
            formatDate = splitDate[0] + ":" + substring1+substring2 + ":" + splitDate[2];
        }
        Date parse = null;
        try {
            parse = sdf.parse(formatDate);

        } catch (ParseException e) {
            e.printStackTrace();
        }
        return new Timestamp(parse.getTime());
    }

    /**
     * @param bytes
     * @return
     */
    public static Date getCarDate2(byte[] bytes, int k) {
        int start = k;
        Calendar cal = Calendar.getInstance();
        cal.clear();
        start += 1;
        //此处忽略高位年，从第1位开始
        cal.set(Calendar.YEAR, bytesToIntHigh(bytes, start, 1) + 2000);
        cal.set(Calendar.MONTH, bytesToIntHigh(bytes, start += 1, 1) - 1);
        cal.set(Calendar.DAY_OF_MONTH, bytesToIntHigh(bytes, start += 1, 1));
        cal.set(Calendar.HOUR_OF_DAY, bytesToIntHigh(bytes, start += 1, 1));
        cal.set(Calendar.MINUTE, bytesToIntHigh(bytes, start += 1, 1));
        cal.set(Calendar.SECOND, bytesToIntHigh(bytes, start + 1, 1));
        return cal.getTime();
    }


    /**
     * @return
     */
    public static byte[] getCarDateByte(Date date) {
        byte[] bytes = new byte[8];
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        bytes[0] = (byte) 0x14;
        bytes[1] = (byte) (cal.get(Calendar.YEAR) - 2000);
        bytes[2] = (byte) (cal.get(Calendar.MONTH) + 1);
        bytes[3] = (byte) cal.get(Calendar.DAY_OF_MONTH);
        bytes[4] = (byte) cal.get(Calendar.HOUR_OF_DAY);
        bytes[5] = (byte) cal.get(Calendar.MINUTE);
        bytes[6] = (byte) cal.get(Calendar.SECOND);
        bytes[7] = (byte) 0x00;
        return bytes;
    }


    /**
     * cp56time2a 转时间，8个字节
     *
     * @param bytes
     * @param start
     * @return
     */
    public static Date cp56time2aToDate(byte[] bytes, int start) {
        if (bytes == null || bytes.length < start + 8) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.clear();
        cal.set(Calendar.YEAR, bytes[start + 7] + 2000);
        cal.set(Calendar.MONTH, bytes[start + 6] - 1);
        cal.set(Calendar.DAY_OF_MONTH, bytes[start + 5]);
        cal.set(Calendar.HOUR_OF_DAY, bytes[start + 4]);
        cal.set(Calendar.MINUTE, bytes[start + 3]);
        cal.set(Calendar.SECOND, bytes[start + 2]);
        cal.set(Calendar.MILLISECOND, bytesToIntHigh(bytes, start, 2));
        return cal.getTime();
    }


    /**
     * 时间转 cp56time2a 字节
     *
     * @param date
     * @return
     */
    public static byte[] dateToCp56time2a(Date date) {
        byte[] bytes = new byte[8];
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        System.arraycopy(ToolsUtil.intToBytesHigh(cal.get(Calendar.MILLISECOND), 2), 0, bytes, 0, 2);
        bytes[2] = (byte) cal.get(Calendar.SECOND);
        bytes[3] = (byte) cal.get(Calendar.MINUTE);
        bytes[4] = (byte) cal.get(Calendar.HOUR_OF_DAY);
        bytes[5] = (byte) cal.get(Calendar.DAY_OF_MONTH);
        bytes[6] = (byte) (cal.get(Calendar.MONTH) + 1);
        bytes[7] = (byte) (cal.get(Calendar.YEAR) - 2000);
        return bytes;
    }

    public static byte getXor2(byte[] datas) {
        byte temp = datas[0];
        for (int i = 1; i < datas.length; i++) {

            temp ^= datas[i];
        }
        return temp;
    }

    /**
     * 亦或校验 带长度
     *
     * @param datas
     * @return
     */
    public static byte getXorLen(byte[] datas, int start, int len) {
        byte temp = datas[start];
        for (int i = start; i < len - 2; i++) {
            temp ^= datas[i + 1];
        }
        return temp;
    }


    /**
     * 亦或校验
     *
     * @param datas
     * @return
     */
    public static byte getXor(byte[] datas, int start) {
        byte temp = datas[start];
        for (int i = start; i < datas.length - 2; i++) {
            temp ^= datas[i + 1];
        }
        return temp;
    }


    /**
     * CRC校验
     *
     * @param bytes
     * @param start
     * @param length
     * @return
     */
    public static byte[] JcCrc16(byte[] bytes, int start, int length) {
        byte[] crcByte = new byte[2];
        if (bytes.length < start + length) {
            return crcByte;
        }

        int CRC = 0xffff;
        int POLYNOMIAL = 0x180D;

        int i, j;
        for (i = start; i < start + length; i++) {
            CRC ^= bytes[i] & 0x00ff;
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x0001) == 0x0001) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        crcByte[1] = (byte) (CRC / 256);
        crcByte[0] = (byte) (CRC % 256);
        return crcByte;
    }

    /**
     * CRC校验
     *
     * @param bytes
     * @param start
     * @param length
     * @return
     */
    public static byte[] crc16(byte[] bytes, int start, int length) {
        byte[] crcByte = new byte[2];
        if (bytes.length < start + length) {
            return crcByte;
        }

        int CRC = 0xffff;
        int POLYNOMIAL = 0xa001;

        int i, j;
        for (i = start; i < start + length; i++) {
            CRC ^= bytes[i] & 0x00ff;
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x0001) == 0x0001) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        crcByte[1] = (byte) (CRC / 256);
        crcByte[0] = (byte) (CRC % 256);
        return crcByte;
    }


    public static String getUID() {
        String uid = UUID.randomUUID().toString().replaceAll("-", "");
        return uid;
    }

    public static synchronized void writeMessage(String log, String address) throws IOException {

        FileWriter fw = null;
        try {
            //如果文件存在，则追加内容；如果文件不存在，则创建文件
            File f = new File(address);
            fw = new FileWriter(f, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        PrintWriter pw = new PrintWriter(fw);
        pw.println(log);
        pw.flush();
        try {
            fw.flush();
            pw.close();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //添加报文头尾及CRC校验位，传入数据为设备编码至数据校验域数据
    public static byte[] addCheck(byte[] bytes, int frameType, String logId) {
        if (bytes == null || bytes.length < 1) {
            return null;
        }
        //设置报文
        byte[] message = new byte[bytes.length + 8];
        //起始帧
        message[0] = (byte) 0xf5;
        message[1] = 0x68;
        //长度帧
        message[2] = (byte) 0xff;
        message[3] = (byte) message.length;
        System.arraycopy(bytes, 0, message, 4, bytes.length);//数据信息
        return message;
    }


    public static String stringToAscii(String value) {
        StringBuffer sbu = new StringBuffer();
        char[] chars = value.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (i != chars.length - 1) {
                sbu.append((int) chars[i]).append(",");
            } else {
                sbu.append((int) chars[i]);
            }
        }
        return sbu.toString();
    }


    /**
     * 时标CP56Time2a解析
     */
    public static String TimeScale(byte[] bytes, int start) {

        String str = "";
        int year = bytesToIntLow(bytes, start + 6, 1) & 0x7F;
        int month = bytesToIntLow(bytes, start + 5, 1) & 0x0F;
        int day = bytesToIntLow(bytes, start + 4, 1) & 0x1F;
//        int week = (b[4] & 0xE0) / 32;
        // int week = (b[4] & 0xE0) >> 5;
        int hour = bytesToIntLow(bytes, start + 3, 1) & 0x1F;
        int minute = bytesToIntLow(bytes, start + 2, 1) & 0x3F;
        int second = (bytesToIntLow(bytes, start + 1, 1) << 8) + bytesToIntLow(bytes, start, 1);

        str += "20" + year + "-"
                + String.format("%02d", month) + "-"
                + String.format("%02d", day) + " " + hour + ":" + minute + ":"
                + second / 1000;
        return str;
    }


    /**
     * 时间转16进制字符串
     */
    public static String date2HStr(Date date) {

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        StringBuilder builder = new StringBuilder();
        String milliSecond = String.format("%04X", (calendar.get(Calendar.SECOND) * 1000) + calendar.get(Calendar.MILLISECOND));
        builder.append(milliSecond.substring(2, 4));
        builder.append(milliSecond.substring(0, 2));
        builder.append(String.format("%02X", calendar.get(Calendar.MINUTE) & 0x3F));
        builder.append(String.format("%02X", calendar.get(Calendar.HOUR_OF_DAY) & 0x1F));
        int week = calendar.get(Calendar.DAY_OF_WEEK);
        if (week == Calendar.SUNDAY)
            week = 7;
        else week--;
        builder.append(String.format("%02X", (week << 5) + (calendar.get(Calendar.DAY_OF_MONTH) & 0x1F)));
        builder.append(String.format("%02X", calendar.get(Calendar.MONTH) + 1));
        builder.append(String.format("%02X", calendar.get(Calendar.YEAR) - 2000));
        return builder.toString();
    }

    /**
     * short转byte
     * @param data
     * @return
     */
    public static byte[] shortToByte(short[] data) {
        byte[] byteValue = new byte[data.length * 2];
        for (int i = 0; i < data.length; i++) {
            byteValue[i * 2] = (byte) (data[i] & 0xff);
            byteValue[i * 2 + 1] = (byte) ((data[i] & 0xff00) >> 8);
        }
        return byteValue;
    }

    /**
     * byte转short
     * @param data
     * @return
     */
    public static short[] byteToShort(byte[] data) {
        short[] shortValue = new short[data.length / 2];
        for (int i = 0; i < shortValue.length; i++) {
            shortValue[i] = (short) ((data[i * 2] & 0xff) | ((data[i * 2 + 1] & 0xff) << 8));
        }
        return shortValue;
    }

}

